class App { constructor( canvasId ) { this.cc = document.getElementById( canvasId ).getContext( "2d" ); let scale = 2; let transX = this.cc.canvas.width / 2 / scale; let transY = -100 ;//this.cc.canvas.height * 0.9 / scale; this.cc.scale( scale, -scale ); this.cc.translate( transX, transY ); this.svcs = new Array(); let svc = new SVC(); let world = new SVC_Part( "world" ); world.addJoint( "世界>頭", 0, 0 ); world.imgd.visibility = false; let atama = new SVC_Part( "頭" ); atama.imgd.w = 20; atama.imgd.h = 20; atama.imgd.x = -10; atama.imgd.y = -20; atama.addJoint( "頭>首", 2.5, -20 ); let kubi = new SVC_Part( "首" ); kubi.imgd.w = 10; kubi.imgd.h = 10; kubi.imgd.x = -5; kubi.imgd.y = -7.5; kubi.addJoint( "首>胸", 0, -4 ); let mune = new SVC_Part( "胸" ); mune.imgd.w = 20; mune.imgd.h = 20; mune.imgd.x = -12.5; mune.imgd.y = -20; mune.addJoint( "胸>腹", 0, -15 ); let hara = new SVC_Part( "腹" ); hara.imgd.w = 18; hara.imgd.h = 15; hara.imgd.x = -12.5; hara.imgd.y = -15; hara.addJoint( "腹>腰", 0, -12.5 ); let kosi = new SVC_Part( "腰" ); kosi.imgd.w = 20; kosi.imgd.h = 15; kosi.imgd.x = -12.5; kosi.imgd.y = -13; kosi.addJoint( "腰>左もも", -2.5, -10 ); kosi.addJoint( "腰>右もも", -2.5, -10 ); let momoL = new SVC_Part( "左もも" ); momoL.imgd.w = 18; momoL.imgd.h = 30; momoL.imgd.x = -10; momoL.imgd.y = -28; momoL.addJoint( "左もも>左すね", -2.5, -27.5 ); let suneL = new SVC_Part( "左もも" ); suneL.imgd.w = 18; suneL.imgd.h = 30; suneL.imgd.x = -6.5; suneL.imgd.y = -28; suneL.addJoint( "左すね>左かかと", 2.5, -25 ); let kakatoL = new SVC_Part( "左かかと" ); kakatoL.imgd.w = 20; kakatoL.imgd.h = 10; kakatoL.imgd.x = -12; kakatoL.imgd.y = -9; kakatoL.addJoint( "左かかと>左つま先", -10, -5 ); let tumasakiL = new SVC_Part( "左つま先" ); tumasakiL.imgd.w = 10; tumasakiL.imgd.h = 7; tumasakiL.imgd.x = -10; tumasakiL.imgd.y = -4; //build. world.connect( "世界>頭", atama ); atama.connect( "頭>首", kubi ); kubi.connect( "首>胸", mune ); mune.connect( "胸>腹", hara ); hara.connect( "腹>腰", kosi ); kosi.connect( "腰>左もも", momoL ); momoL.connect( "左もも>左すね", suneL ); suneL.connect( "左すね>左かかと", kakatoL ); kakatoL.connect( "左かかと>左つま先", tumasakiL ); atama.rotation = -3.14 / 2; kubi.rotation = 3.14 /8; hara.rotation = 3.14 /8; kosi.rotation = 3.14 /8; momoL.rotation = 3.14 /4; suneL.rotation = 3.14 /4; kakatoL.rotation = 3.14 /4; tumasakiL.rotation = -3.14 /4; svc.setTopPart( world ); svc.calc(); this.svcs.push( svc ); } start() { if( 1 ) { this.draw( this.cc ); } if( 0 ) { this.timerId = setInterval( function() { this.draw( this.cc ); this.flg = ! this.flg; }.bind( this ), 500 ); } } stop() { clearInterval( this.timerId ); } draw( cc ) { for( let i = 0; i < this.svcs.length; i++ ) { let svc = this.svcs[ i ]; svc.draw( cc ); } } }//App class SVC { constructor() { this.topPart = null; this.x = 0; this.y = 0; } setTopPart( topPart ) { this.topPart = topPart; } calc() { this.topPart.calc(); } draw( cc ) { this.topPart.draw( cc ); } } class SVC_Part { constructor( title ) { this.title = title; this.jointHash = new Object(); this.rotation = 0; this.abs = new Object(); this.parent = null; this.parentJoint = null; this.children = new Array(); this.imgd = new TestRect(); } addJoint( name, x, y ) { let joint = new Object(); joint.name = name; joint.x = x; joint.y = y; this.jointHash[ name ] = joint; } //他のSVC_Partを自身のJointへ接続 connect( jointName, part ) { this.children.push( part ); part.parent = this; part.parentJoint = this.jointHash[ jointName ]; } calc() { //自身の値の絶対座標を求める。 if( ! this.parent ) { //親がない場合は this.abs.x = 0; this.abs.y = 0; this.abs.rotation = this.rotation; } else { //親がある場合は //check. if( ! this.parentJoint ) { alert( "undefined parentJoint at " + this.title ); } let parentJointAbs = this.parent.abs.jointHash[ this.parentJoint.name ]; this.abs.x = parentJointAbs.x; this.abs.y = parentJointAbs.y; this.abs.rotation = this.parent.abs.rotation + this.rotation; } //各jointの絶対座標を求める。 this.abs.jointHash = new Object(); for( let name in this.jointHash ) { let j = this.jointHash[ name ]; let res = this.mathRotate( j.x, j.y, this.abs.rotation ); this.abs.jointHash[ name ] = { x : this.abs.x + res.X, y : this.abs.y + res.Y, } } //子もcalc() for( let i = 0; i < this.children.length; i++ ) { let child = this.children[ i ]; child.calc(); } }//calc() //SVC_Part draw( cc ) { console.log( this.title + " draw." ); //自身を描画 cc.save(); cc.translate( this.abs.x, this.abs.y ); cc.rotate( this.abs.rotation ); cc.translate( this.imgd.x, this.imgd.y ); this.imgd.draw( cc ); cc.restore(); //子を描画 for( let i = 0; i < this.children.length; i++ ) { let child = this.children[ i ]; child.draw( cc ); } //debug. if( 1 ) { //各jointを○で示す for( let name in this.jointHash ) { let joint = this.abs.jointHash[ name ]; cc.save(); cc.translate( joint.x, joint.y ); cc.scale( 1, -1 ); cc.globalAlpha = .3; cc.beginPath(); cc.arc( 0, 0, 3, 0, 6.28, false ); cc.closePath(); cc.strokeStyle = "red"; cc.stroke(); cc.font = "6px''"; cc.fillText( name, 0, 0 ); cc.restore(); } } }//draw() //--- mathRotate( x, y, theta2 ) { let theta1 = Math.atan2( y, x ); let hankei = Math.sqrt( x * x + y * y ); return { X : Math.cos( theta1 + theta2 ) * hankei, Y : Math.sin( theta1 + theta2 ) * hankei, } } }//SVC_Part class TestRect { constructor() { this.x = 0; this.y = 0; this.w = 10; this.h = 10; this.visibility = true; } draw( cc ) { //check. if( ! this.visibility ) return; cc.strokeRect( 0, 0, this.w, this.h ); } }